import base64
import json
import pickle

from django import http
from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connection
from apps.carts import constants
from apps.goods.models import SKU
from utils.response_code import RETCODE
import logging
logger = logging.getLogger('django')

# 定义一个类, 处理购物车的增删改查
class CartsView(View):
    '''
    前端逻辑
    接收商品id 数量 是否选中

    后端逻辑
        1. 接受数据
        2. 验证数据
        3. 判断用户是否登陆
        4. 用户登录保存redis中
            4.1 连接redis
            4.2 hash
            4.3 set
            4.4 返回响应
        5. 未登录用户保存在cookie中
            5.1 组织字典数据
            5.2 对字典数据进行base64处理
            5.3 写入cookie
            5.4 返回响应

    请求方式和路径
    POST	   carts/
    '''
    # 添加购物车
    def post(self, request):
        # 1. 接受数据
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        selected = json_dict.get('selected', True)

        # 2. 验证数据
        if not all([sku_id, count, selected]):
            return http.HttpResponseBadRequest('缺少必传参数')
        # 判断sku_id是否存在
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseBadRequest('商品不存在')
        # 判断count是否为数字
        try:
            count = int(count)
        except Exception:
            return http.HttpResponseBadRequest('参数count有误')
        # 判断selected是否为bool值
        if selected:
            if not isinstance(selected, bool):
                return http.HttpResponseBadRequest('参数selected有误')

        # 3. 判断用户是否登陆
        user = request.user
        if user.is_authenticated:
            # 4. 用户登录保存redis中
            #     4.1 连接redis
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            #     4.2 hash 新增购物车数据
            pl.hset('carts_%s' % user.id, sku_id, count)
            #     4.3 set  新增选中状态
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)
            pl.execute()
            #     4.4 返回响应
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '添加购物车成功'})

        else:
            # 5. 未登录用户保存在cookie中
            cart_str = request.COOKIES.get('carts')
            # 如果未登录用户操作过购物车
            if cart_str:
                # 将cart_str转化为bytes, 再将bytes转化为base64的bytes, 最后将bytes转化为字典
                cart_dict = pickle.loads(base64.b64decode(cart_str))
            else:
                # 用户没操作过购物车
                cart_dict = {}

            #     5.1 组织字典数据
            # 判断要加入购物车的商品是否已经在购物车中,如有相同商品，累加求和，反之，直接赋值
            if sku_id in cart_dict:
                # 累加求和
                origin_count = cart_dict[sku_id]['count']
                count += origin_count
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }
            #     5.2 对字典数据进行base64处理
            # 将字典转成bytes,再将bytes转成base64的bytes,最后将bytes转字符串
            cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            #     5.3 写入cookie
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': '添加购物车成功'})
            response.set_cookie('carts', cookie_cart_str, max_age=constants.CARTS_COOKIE_EXPIRES)
            #     5.4 返回响应
            return response


    '''
    前端逻辑
        获取用户的信息

    后端逻辑
        1. 获取用户信息
        2. 判断用户是否登陆
        3. 已登录用户从redis中获取数据
            3.1 连接redis
            3.2 hash
            3.3 set
            3.4 对商品id进行遍历
            3.5 根据商品id得到商品信息
            3.6 将对象转换为字典
            3.7 返回响应
        4. 未登录用户从cookie中获取数据
            4.1 读取cookie中的数据
            4.2 判断cookie数据
            4.3 获取商品id
            4.4 对商品id进行遍历
            4.5 根据商品id获取商品信息
            4.6将对象转换为字典
            4.7 返回响应

    请求方式和路径
    GET                     carts/
    '''
    # 展示购物车
    def get(self, request):
        # 1. 获取用户信息
        user = request.user
        # 2. 判断用户是否登陆
        if user and user.is_authenticated:
            # 3. 已登录用户从redis中获取数据
            #     3.1 连接redis
            redis_conn = get_redis_connection('carts')
            #     3.2 hash 购物车数据
            redis_cart = redis_conn.hgetall('carts_%s' % user.id)
            #     3.3 set 购物车勾选状态
            cart_selected = redis_conn.smembers('selected_%s' % user.id)

            cart_dict = {}
            #     3.4 对商品id 和数量进行遍历
            for sku_id, count in redis_cart.items():
                #     3.5 根据商品id得到商品信息
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in cart_selected
                }

        else:
            # 4. 未登录用户从cookie中获取数据
            #     4.1 读取cookie中的数据
            cart_str = request.COOKIES.get('carts')
            #     4.2 判断cookie数据
            if cart_str:
                # 对cookie中的数据进行解码
                cart_dict = pickle.loads(base64.b64decode(cart_str))
            else:
                cart_dict = {}
        #     5 获取商品id
        sku_ids = cart_dict.keys()
        cart_skus = []
        #     6 对商品id进行遍历
        for id in sku_ids:
            #     7 根据商品id获取商品信息
            sku = SKU.objects.get(pk=id)
            #     8 将对象转换为字典
            cart_skus.append({
                'id': sku.id,
                'name': sku.name,
                'count': cart_dict.get(sku.id).get('count'),
                'selected': str(cart_dict.get(sku.id).get('selected')),  # 将True，转'True'，方便json解析
                'default_image_url': sku.default_image.url,
                'price': str(sku.price),  # 从Decimal('10.2')中取出'10.2'，方便json解析
                'amount': str(sku.price * cart_dict.get(sku.id).get('count')),
            })

        context = {
            'cart_skus': cart_skus
        }
        #     9 返回响应
        return render(request, 'cart.html', context)


    '''
    前端逻辑
    获取修改商品id

    后端逻辑
        1. 获取修改商品数据并判断数据
        2. 获取用户信息, 判断用户是否登陆
        3. 登陆用户修改redis中的数据
            3.1 连接redis
            3.2 修改商品id和个数   hash
            3.3 修改商品是否勾选   set
            3.4 返回响应
        4. 未登录用户修改cookie中的数据
            4.1 获取cookie中的数据
            4.2 判断cookie数据
            4.3 修改商品数据
            4.4 加密处理
            4.5 写入cookie
            4.6 返回响应
    请求方式和路径
    PUT                     carts/
    '''
    # 修改购物车
    def put(self,request):
        # 1.获取修改商品数据并判断数据
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        selected = json_dict.get('selected', True)

        if not all([sku_id, count]):
            return http.HttpResponseBadRequest('缺少必要参数')

        try:
            # 判断sku_id是否存在
            sku = SKU.objects.get(pk=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseBadRequest('商品不存在')

        try:
            # 判断count是否为数字
            count = int(count)
        except Exception as e:
            logger.error(e)
            return http.HttpResponseBadRequest('count有错误')

        # 判断selected是否为bool
        if selected:
            if not isinstance(selected, bool):
                return http.HttpResponseBadRequest('selected有错误')

        # 2.获取用户信息, 判断用户是否登陆
        user = request.user
        if user and user.is_authenticated:
            # 3. 登陆用户修改redis中的数据
            #     3.1 连接redis
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            # 修改数据可以直接覆盖
            #     3.2 修改商品id和个数   hash
            pl.hset('carts_%s' % user.id, sku_id, count)
            #     3.3 修改商品是否勾选   set
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)
            else:
                pl.srem('selected_%s' % user.id, sku_id)
            pl.execute()
            #     3.4 返回响应
            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price,
                'amount': sku.price * count,
            }

            return http.JsonResponse({'code':RETCODE.OK, 'errmsg':'修改购物车成功', 'cart_sku':cart_sku})

        else:
            # 4. 未登录用户修改cookie中的数据
            #     4.1 获取cookie中的数据
            cart_str = request.COOKIES.get('carts')
            #     4.2 判断cookie数据
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str))
            else:
                cart_dict = {}
            #     4.3 修改商品数据  直接覆盖
            cart_dict[sku_id] = {
                'count':count,
                'selected': selected
            }
            #     4.4 加密处理
            cookie_cart = base64.b64encode(pickle.dumps(cart_dict))
            #     4.5 写入cookie
            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price,
                'amount': sku.price * count,
            }
            response = http.JsonResponse({'code':RETCODE.OK, 'errmsg':'修改购物车成功', 'cart_sku':cart_sku})
            response.set_cookie('carts', cookie_cart, max_age=constants.CARTS_COOKIE_EXPIRES)
            #     4.6 返回响应
            return response


    '''
    前端逻辑
    获取删除商品id

    后端逻辑
    0. 获取删除数据信息并验证
    1. 获取用户信息
    2. 判断用户是否登陆
    3. 登录用户操作redis
        3.1 连接redis
        3.2 商品id 个数  hash
        3.3 是否勾选   set
        3.4 返回响应
    4. 未登录用户操作cookie
        4.1 获取cookie信息并判断
        4.2 判断商品id是否在cookie中
        4.3 如果存在就删除
        4.4 cookie数据加密
        4.5 返回响应

    请求方式和路径
    DELETE                 carts/
    '''
    # 删除购物车
    def delete(self, request):
        # 0. 获取删除数据信息并验证
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        try:
            SKU.objects.get(pk=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseBadRequest('此商品不存在')
        # 1. 获取用户信息
        user = request.user
        # 2. 判断用户是否登陆
        if user and user.is_authenticated:
            # 3. 登录用户操作redis
            #     3.1 连接redis
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            #     3.2 商品id 个数  hash
            pl.hdel('carts_%s' % user.id, sku_id)
            #     3.3 是否勾选   set
            pl.srem('selected_%s' % user.id, sku_id)
            pl.execute()
            #     3.4 返回响应
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '删除购物车成功'})
        else:
            # 4. 未登录用户操作cookie
            #     4.1 获取cookie信息并判断
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str))
            #     4.2 判断商品id是否在cookie中
            else:
                cart_dict = {}
            #     4.3 如果存在就删除
            if sku_id in cart_dict:
                del cart_dict[sku_id]
            #     4.4 cookie数据加密
            cookie_cart = base64.b64encode(pickle.dumps(cart_dict))
            #     4.5 返回响应
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': '删除购物车成功'})
            # 写入cookie
            response.set_cookie('carts', cookie_cart, max_age=constants.CARTS_COOKIE_EXPIRES)
            return response



'''
前端逻辑
接收是否勾选数据

后端逻辑
    1. 接受数据并验证
    2. 判断用户是否登陆
    3. 登录操作redis
        3.1 连接redis并验证数据
        3.2 修改勾选数据   set
        3.3 返回响应
    4. 未登录操作cookie
        4.1 读取cookie数据并验证
        4.2 修改勾选数据
        4.3 加密数据
        4.4 写入cookie
        4.5 返回响应

请求方式和路径
PUT			 carts/selection/
'''
# 定义一个类, 处理勾选购物车业务
class CartSelectView(View):

    def put(self, request):
        # 1. 接受数据并验证
        json_dict = json.loads(request.body.decode())
        selected = json_dict.get('selected', True)
        if selected:
            if not isinstance(selected, bool):
                return http.HttpResponseBadRequest('参数selected有误')
        # 2. 判断用户是否登陆
        user = request.user
        if user and user.is_authenticated:
            # 3. 登录操作redis
            #     3.1 连接redis并验证数据
            redis_conn = get_redis_connection('carts')
            #     3.3 获取购物车商品id  hash
            cart = redis_conn.hgetall('carts_%s' % user.id)
            sku_id_list = cart.keys()
            #     3.3 修改勾选数据   set
            if selected:
                # 全选
                redis_conn.sadd('selected_%s' % user.id, *sku_id_list)
            else:
                # 取消全选
                redis_conn.srem('selected_%s' % user.id, *sku_id_list)
            #     3.4 返回响应
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '全选购物车成功'})
        else:
            # 4. 未登录操作cookie
            #     4.1 读取cookie数据并验证
            cart = request.COOKIES.get('carts')
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': '全选购物车成功'})
            if cart:
                cart_str = pickle.loads(base64.b64decode(cart))
                #     4.2 修改勾选数据
                for sku_id in cart_str:
                    cart_str[sku_id]['selected'] = selected
                #     4.3 加密数据
                cookie_cart = base64.b64encode(pickle.dumps(cart_str))
                #     4.4 写入cookie
                response.set_cookie('carts', cookie_cart, max_age=constants.CARTS_COOKIE_EXPIRES)
            #     4.5 返回响应
            return response


'''
前端逻辑
    获取用户信息

后端逻辑
    1. 获取用户数据
    2. 判断用户是否登陆
    3. 登陆用户从redis中提取数据
        3.1 连接redis
        3.2 hash  商品id和数量
        3.3 set    商品勾选状态(勾选状态可以不传, 简单购物车勾不勾选都显示该商品)
        3.4 重新组织数据到一个新的字典
    4. 未登录用户从cookie中提取数据
        4.1 获取cookie数据并判断
        4.2 如果有就解码, 没有就定义一个新的字典
    5. 获取商品id
    6. 以商品id遍历字典
    7. 构建返回响应数据
8. 返回响应

请求方式和路径
GET  	  carts/simple/
'''
# 定义一个类, 处理首页简单购物车的实现
class CartsSimpleView(View):
    def get(self, request):
        # 1. 获取用户数据
        user = request.user

        # 2. 判断用户是否登陆
        if user and user.is_authenticated:
            # 3.登陆用户从redis中提取数据
            # 3.1连接redis
            redis_conn = get_redis_connection('carts')
            # 3.2 hash商品id和数量
            redis_cart = redis_conn.hgetall('carts_%s' % user.id)
            # 3.3 set商品勾选状态
            redis_cart_selected = redis_conn.smembers('selected_%s' % user.id)
            # 3.4 重新组织数据到一个新的字典
            cart_dict = {}
            for sku_id, count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    # 勾选状态可以不传, 简单购物车勾不勾选都显示该商品
                    'selected': sku_id in redis_cart_selected
                }

        else:
            # 4. 未登录用户从cookie中提取数据
            # 4.1 获取cookie数据并判断
            # 4.2 如果有就解码, 没有就定义一个新的字典
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str))
            else:
                cart_dict = {}

        cart_skus = []
        # 5. 获取商品id
        sku_ids = cart_dict.keys()
        # 6. 以商品id遍历字典
        for id in sku_ids:
            sku = SKU.objects.get(pk=id)
            # 7. 构建返回响应数据
            cart_skus.append({
                'id': sku.id,
                'name': sku.name,
                'count': cart_dict.get(sku.id).get('count'),
                'default_image_url': sku.default_image.url
            })

        # 8. 返回响应
        return http.JsonResponse({'code':RETCODE.OK, 'errmsg':'OK', 'cart_skus':cart_skus})